/**
 * @file truth_table.c
 * @brief function definitions for manipulating truth tables
 * @author Scott Moyers
 * @date 2009
 */

#include "truth_table.h"
#include "module.h" /* for signals */
#include "util.h"

#include <string.h>
#include <assert.h>
#include <math.h>

struct TruthTable *new_truth_table(unsigned int inputs, unsigned int outputs)
{
	struct TruthTable *truth = (struct TruthTable *) smalloc(sizeof(struct TruthTable));
	assert(inputs < 32);
	truth->nins = inputs;
	truth->nouts = outputs;
	truth->signals = malloc_signals(outputs, signal_width(inputs));
	return truth;
}

struct TruthTable *new_truth_table_from_file(const char *f)
{
	struct TruthTable *t;
	int in, out;
	unsigned int i, j, n;
	int c;
	FILE *fp = fopen(f, "r");

	if (fp == NULL) {
		printf("Error: unable to open file %s for reading\n", f);
		return NULL;
	}

	n = fscanf(fp, "%d%d", &in, &out);
	if (n != 2) {
		printf("Error: Unable to read port sizes File:%s\n", f);
		fclose(fp);
		return NULL;
	}

	if (in >= 32) {
		printf("Error: input size %d >= 32 File:%s\n", in, f);
		fclose(fp);
		return NULL;
	}

	if (out >= 32) {
		printf("Error: output size %d >= 32 File:%s\n", out, f);
		fclose(fp);
		return NULL;
	}

	t = new_truth_table(in, out);

	/* clear all signals */
	for(i = 0; i < (unsigned) out; i++) {
		for(j = 0; j < signal_width(in); j++) {
			t->signals[i][j] = 0;
		}
	}

	for(i = 0; i < ((unsigned)1 << in); i++) {
		for(j = 0; j < (unsigned) out;) {
			c = fgetc(fp);
			switch (c) {
			case '\n':
			case ' ' :
			case '\r':
			case '\t':
				break;
			case '0':
				j++;
				break;
			case '1':
				set_signal_bit(t->signals[j], i);
				j++;
				break;
			case EOF:
				printf("Error: Premature end of file File:%s\n", f);
				free_truth_table(t);
				fclose(fp);
				return NULL;
			default:
				printf("Error: invalid character %c File:%s\n", c, f);
				free_truth_table(t);
				fclose(fp);
				return NULL;
			}
		}
	}
	fclose(fp);
	return t;
}

void free_truth_table(struct TruthTable * table)
{
	if (table != NULL) {
		free_signals(table->signals, table->nouts);
		free(table);
	}
}

void print_truth_table(FILE * fp, struct TruthTable * table)
{
	unsigned int i;
	for(i = 0; i < table->nouts; i++) {
		fprint_signal(fp, table->signals[i], signal_width(table->nins));
		printf("\n");
	}
}

struct TruthTable *new_rand_truth_table(dsfmt_t *dsfmt, unsigned int inputs, unsigned int outputs)
{
	unsigned int i;
	struct TruthTable *t = new_truth_table(inputs, outputs);
	for(i = 0; i < outputs; i++) {
		unsigned int j;
		for(j = 0; j < signal_width(inputs); j++) {
			t->signals[i][j] = dsfmt_genrand_uint32(dsfmt);
		}
		mask_signal(t->signals[i], inputs);
	}
	return t;
}

struct TruthTable *copy_truth_table(struct TruthTable *t)
{
	unsigned int i;
	struct TruthTable *cpy = new_truth_table(t->nins, t->nouts);
	for(i = 0; i < t->nouts; i++) {
		memcpy(cpy->signals[i], t->signals[i],
		       sizeof(unsigned int) * signal_width(t->nins));
	}
	return cpy;
}
